import sys
import os
import json
import random
import time
import datetime
import requests
import hashlib
import hmac
import secrets
from faker import Faker
from pymongo import MongoClient
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, 
                             QLabel, QLineEdit, QPushButton, QTabWidget, QComboBox, 
                             QSpinBox, QDateEdit, QTableWidget, QTableWidgetItem, QHeaderView,
                             QMessageBox, QProgressBar, QGroupBox, QFormLayout, QCheckBox,
                             QTextEdit, QScrollArea, QSizePolicy, QFrame, QDialog, QDialogButtonBox)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QDate, QSettings
from PyQt5.QtGui import QFont, QIcon, QPalette, QColor
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker
import pymysql
import pymysql.cursors

# 服务器配置
DEFAULT_SERVER_URL = "https://smartsound.top/api"

# MongoDB集合配置
MONGODB_COLLECTIONS = {
    "device_status": "device_status",
    "device_bind_logs": "device_bind_logs",
    "user_logs": "user_logs",
    "listen_records": "listen_records",
    "daily_preferences": "daily_preferences",
    "monthly_preferences": "monthly_preferences",
    "yearly_preferences": "yearly_preferences",
    "daily_duration": "daily_duration",
    "monthly_duration": "monthly_duration",
    "yearly_duration": "yearly_duration",
    "daily_period_stats": "daily_period_stats",
    "monthly_period_stats": "monthly_period_stats",
    "yearly_period_stats": "yearly_period_stats",
    "admin_logs": "admin_logs",
    "user_feedback": "user_feedback",
    "feedback_replies": "feedback_replies",
    "announcements": "announcements"
}

# 初始化Faker
fake = Faker(['zh_CN', 'en_US'])

class ConfigDialog(QDialog):
    """配置对话框"""
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("后端与数据库配置")
        self.setMinimumWidth(400)
        self.init_ui()
        self.load_settings()
    
    def init_ui(self):
        layout = QVBoxLayout()
        
        # 后端配置组
        backend_group = QGroupBox("后端配置")
        backend_layout = QFormLayout()
        
        self.backend_url = QLineEdit()
        
        backend_layout.addRow("后端服务地址:", self.backend_url)
        
        backend_group.setLayout(backend_layout)
        
        # MySQL配置组
        mysql_group = QGroupBox("MySQL配置")
        mysql_layout = QFormLayout()
        
        self.mysql_host = QLineEdit()
        self.mysql_port = QLineEdit()
        self.mysql_user = QLineEdit()
        self.mysql_password = QLineEdit()
        self.mysql_password.setEchoMode(QLineEdit.Password)
        self.mysql_database = QLineEdit()
        
        mysql_layout.addRow("主机:", self.mysql_host)
        mysql_layout.addRow("端口:", self.mysql_port)
        mysql_layout.addRow("用户名:", self.mysql_user)
        mysql_layout.addRow("密码:", self.mysql_password)
        mysql_layout.addRow("数据库:", self.mysql_database)
        
        mysql_group.setLayout(mysql_layout)
        
        # MongoDB配置组
        mongo_group = QGroupBox("MongoDB配置")
        mongo_layout = QFormLayout()
        
        self.mongo_host = QLineEdit()
        self.mongo_port = QLineEdit()
        self.mongo_user = QLineEdit()
        self.mongo_password = QLineEdit()
        self.mongo_password.setEchoMode(QLineEdit.Password)
        self.mongo_database = QLineEdit()
        self.mongo_auth_source = QLineEdit()
        
        mongo_layout.addRow("主机:", self.mongo_host)
        mongo_layout.addRow("端口:", self.mongo_port)
        mongo_layout.addRow("用户名:", self.mongo_user)
        mongo_layout.addRow("密码:", self.mongo_password)
        mongo_layout.addRow("数据库:", self.mongo_database)
        mongo_layout.addRow("认证数据库:", self.mongo_auth_source)
        
        mongo_group.setLayout(mongo_layout)
        
        # 按钮
        button_box = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel
        )
        button_box.accepted.connect(self.accept)
        button_box.rejected.connect(self.reject)
        
        # 添加到主布局
        layout.addWidget(backend_group)
        layout.addWidget(mysql_group)
        layout.addWidget(mongo_group)
        layout.addWidget(button_box)
        
        self.setLayout(layout)
    
    def load_settings(self):
        """加载保存的设置"""
        settings = QSettings("SmartSound", "DataGenerator")
        
        # 后端设置
        self.backend_url.setText(settings.value("server/url", DEFAULT_SERVER_URL))
        
        # MySQL设置
        self.mysql_host.setText(settings.value("mysql/host", "47.108.81.229"))
        self.mysql_port.setText(settings.value("mysql/port", "3306"))
        self.mysql_user.setText(settings.value("mysql/user", "Smart_Sounddb"))
        self.mysql_password.setText(settings.value("mysql/password", "123456"))
        self.mysql_database.setText(settings.value("mysql/database", "Smart_Sounddb"))
        
        # MongoDB设置
        self.mongo_host.setText(settings.value("mongo/host", "47.108.81.229"))
        self.mongo_port.setText(settings.value("mongo/port", "27017"))
        self.mongo_user.setText(settings.value("mongo/user", "smartsound"))
        self.mongo_password.setText(settings.value("mongo/password", "123456"))
        self.mongo_database.setText(settings.value("mongo/database", "smartsound"))
        self.mongo_auth_source.setText(settings.value("mongo/auth_source", "smartsound"))
    
    def save_settings(self):
        """保存设置"""
        settings = QSettings("SmartSound", "DataGenerator")
        
        # 后端设置
        settings.setValue("server/url", self.backend_url.text())
        
        # MySQL设置
        settings.setValue("mysql/host", self.mysql_host.text())
        settings.setValue("mysql/port", self.mysql_port.text())
        settings.setValue("mysql/user", self.mysql_user.text())
        settings.setValue("mysql/password", self.mysql_password.text())
        settings.setValue("mysql/database", self.mysql_database.text())
        
        # MongoDB设置
        settings.setValue("mongo/host", self.mongo_host.text())
        settings.setValue("mongo/port", self.mongo_port.text())
        settings.setValue("mongo/user", self.mongo_user.text())
        settings.setValue("mongo/password", self.mongo_password.text())
        settings.setValue("mongo/database", self.mongo_database.text())
        settings.setValue("mongo/auth_source", self.mongo_auth_source.text())
    
    def accept(self):
        """确认按钮处理"""
        self.save_settings()
        super().accept()

class ApiClient:
    """API客户端，处理与服务器的通信"""
    
    def __init__(self):
        self.server_url = DEFAULT_SERVER_URL
        self.token = None
        self.refresh_token = None
        self.token_expires_at = None
        self.device_id = None
        self.user_id = None
        self.device_key = None
        self.load_settings()
    
    def load_settings(self):
        """加载服务器配置"""
        settings = QSettings("SmartSound", "DataGenerator")
        self.server_url = settings.value("server/url", DEFAULT_SERVER_URL)
    
    def get_users_and_devices(self):
        """获取数据库中已绑定设备的用户信息"""
        try:
            settings = QSettings("SmartSound", "DataGenerator")
            mysql_host = settings.value("mysql/host", "47.108.81.229")
            mysql_port = settings.value("mysql/port", "3306")
            mysql_user = settings.value("mysql/user", "Smart_Sounddb")
            mysql_password = settings.value("mysql/password", "123456")
            mysql_database = settings.value("mysql/database", "Smart_Sounddb")
            
            # 构建MySQL连接URI
            mysql_uri = f"mysql+pymysql://{mysql_user}:{mysql_password}@{mysql_host}:{mysql_port}/{mysql_database}"
            
            # 连接MySQL
            engine = create_engine(mysql_uri)
            Session = sessionmaker(bind=engine)
            session = Session()
            
            # 查询已绑定设备的用户信息
            query = text("""
                SELECT u.user_id, u.user_name, d.device_id, d.device_key
                FROM user u
                JOIN device d ON u.user_id = d.user_id
                WHERE d.is_activated = TRUE
                AND u.is_active = TRUE
            """)
            result = session.execute(query)
            
            users_devices = []
            for row in result:
                users_devices.append({
                    'user_id': row.user_id,
                    'username': row.user_name,
                    'device_id': row.device_id,
                    'device_key': row.device_key
                })
            
            session.close()
            return {"code": 0, "data": users_devices}
        except Exception as e:
            return {"code": 500, "message": f"获取用户和设备信息失败: {str(e)}"}
    
    def clear_mongodb_collections(self):
        """直接清除所有MongoDB集合数据"""
        try:
            settings = QSettings("SmartSound", "DataGenerator")
            mongo_host = settings.value("mongo/host", "47.108.81.229")
            mongo_port = settings.value("mongo/port", "27017")
            mongo_db = settings.value("mongo/database", "smartsound")
            mongo_user = settings.value("mongo/user", "smartsound")
            mongo_password = settings.value("mongo/password", "123456")
            mongo_auth_source = settings.value("mongo/auth_source", "smartsound")
            
            # 构建MongoDB连接URI
            mongo_uri = f"mongodb://{mongo_user}:{mongo_password}@{mongo_host}:{mongo_port}/{mongo_db}?authSource={mongo_auth_source}"
            
            # 连接MongoDB
            client = MongoClient(mongo_uri)
            db = client[mongo_db]
            
            # 清除所有集合数据
            for collection_name in MONGODB_COLLECTIONS.values():
                try:
                    collection = db[collection_name]
                    result = collection.delete_many({})
                    print(f"Cleared {collection_name}: {result.deleted_count} documents")
                except Exception as e:
                    print(f"Error clearing {collection_name}: {str(e)}")
            
            client.close()
            return {"code": 0, "message": "MongoDB数据清除成功"}
        except Exception as e:
            return {"code": 500, "message": f"MongoDB操作失败: {str(e)}"}
    
    def register_device(self, device_id, device_key):
        """注册新设备"""
        try:
            url = f"{self.server_url}/v1/device_register"
            data = {
                "device_id": device_id,
                "device_key": device_key
            }
            response = requests.post(url, json=data)
            return response.json()
        except Exception as e:
            return {"code": 500, "message": f"请求失败: {str(e)}"}
    
    def register_user(self, username, password="123456"):
        """注册新用户"""
        try:
            url = f"{self.server_url}/v1/register"
            data = {
                "username": username,
                "password": password
            }
            response = requests.post(url, json=data)
            return response.json()
        except Exception as e:
            return {"code": 500, "message": f"请求失败: {str(e)}"}
    
    def bind_device(self, device_id, user_id, secret):
        """绑定设备"""
        try:
            url = f"{self.server_url}/v1/bind"
            timestamp = int(time.time() * 1000)  # 13位毫秒级时间戳
            data = {
                "device_id": device_id,
                "user_id": user_id,
                "secret": secret,
                "timestamp": timestamp
            }
            response = requests.post(url, json=data)
            if response.status_code == 200:
                self.device_id = device_id
                self.user_id = user_id
                self.device_key = secret
            return response.json()
        except Exception as e:
            return {"code": 500, "message": f"请求失败: {str(e)}"}
    
    def get_ticket(self, device_id):
        """获取票据"""
        try:
            url = f"{self.server_url}/v1/ticket"
            timestamp = int(time.time() * 1000)  # 13位毫秒级时间戳
            data = {
                "device_id": device_id,
                "timestamp": timestamp
            }
            response = requests.post(url, json=data)
            response_data = response.json()
            return response_data
        except Exception as e:
            return {"code": 500, "message": f"请求失败: {str(e)}"}
    
    def get_token(self, ticket, device_id, secret):
        """获取令牌"""
        try:
            url = f"{self.server_url}/v1/token"
            timestamp = int(time.time() * 1000)  # 13位毫秒级时间戳
            
            # 生成签名
            # 修正签名生成方式，与后端保持一致
            sign_str = f"{ticket}{device_id}{secret}"
            signature = hashlib.md5(sign_str.encode()).hexdigest()
            
            data = {
                "ticket": ticket,
                "device_id": device_id,
                "signature": signature,
                "timestamp": timestamp
            }
            response = requests.post(url, json=data)
            result = response.json()
            
            if isinstance(result, dict) and result.get("code") == 0:
                self.token = result.get("token")
                self.refresh_token = result.get("refreshToken")
                # 设置token过期时间（小时转换为秒）
                expires_in = result.get("expiresIn", 3600)  # 默认1小时
                self.token_expires_at = time.time() + expires_in
                self.device_id = device_id
                self.device_key = secret
            else:
                # 认证失败时输出调试信息
                print(f"认证失败: {result}")
                print(f"签名详情: 签名字符串={sign_str}, 生成的签名={signature}")
                print(f"请求数据: {data}")
            return result
        except Exception as e:
            return {"code": 500, "message": f"请求失败: {str(e)}"}
    
    def refresh_auth_token(self):
        """刷新认证令牌"""
        try:
            if not self.refresh_token or not self.device_id or not self.device_key:
                return {"code": 400, "message": "缺少刷新令牌所需信息"}
                
            url = f"{self.server_url}/v1/token/refresh"
            timestamp = int(time.time())
            
            # 生成签名
            sign_str = f"{self.refresh_token}{self.device_id}{self.device_key}"
            signature = hashlib.md5(sign_str.encode()).hexdigest()
            
            data = {
                "refreshToken": self.refresh_token,
                "device_id": self.device_id,
                "signature": signature,
                "timestamp": timestamp
            }
            
            response = requests.post(url, json=data)
            result = response.json()
            
            if isinstance(result, dict) and result.get("code") == 0:
                self.token = result.get("token")
                self.refresh_token = result.get("refreshToken")
                # 设置token过期时间（小时转换为秒）
                expires_in = result.get("expiresIn", 3600)  # 默认1小时
                self.token_expires_at = time.time() + expires_in
            else:
                # 认证失败时输出调试信息
                print(f"刷新令牌失败: {result}")
                print(f"签名详情: 签名字符串={sign_str}, 生成的签名={signature}")
                print(f"请求数据: {data}")
                
            return result
        except Exception as e:
            return {"code": 500, "message": f"刷新令牌失败: {str(e)}"}
    
    def ensure_valid_token(self):
        """确保有效的认证令牌"""
        # 如果没有令牌或令牌即将过期（提前5分钟刷新）
        if not self.token or not self.token_expires_at or time.time() > (self.token_expires_at - 300):
            if self.refresh_token:
                # 尝试刷新令牌
                result = self.refresh_auth_token()
                if isinstance(result, dict) and result.get("code") == 0:
                    return True
            
            # 如果没有刷新令牌或刷新失败，尝试重新获取令牌
            if self.device_id and self.device_key:
                # 先获取ticket
                ticket_result = self.get_ticket(self.device_id)
                if isinstance(ticket_result, dict) and ticket_result.get("code") == 0:
                    ticket = ticket_result.get("ticket")
                    if ticket:
                        # 使用ticket获取token
                        token_result = self.get_token(ticket, self.device_id, self.device_key)
                        return isinstance(token_result, dict) and token_result.get("code") == 0
            
            return False
        return True
    
    def upload_listen_data(self, device_id, user_id, song_id, song_name, artists, duration):
        """上传收听数据"""
        try:
            url = f"{self.server_url}/v2/data"
            timestamp = int(time.time() * 1000)  # 13位毫秒级时间戳
            data = {
                "device_id": device_id,
                "user_id": user_id,
                "timestamp": timestamp,
                "id": song_id,
                "song_name": song_name,
                "artists": artists,
                "dt": duration * 1000  # 转换为毫秒
            }
            # 听歌记录不需要认证
            headers = {}
            response = requests.post(url, json=data, headers=headers)
            return response.json()
        except Exception as e:
            return {"code": 500, "message": f"请求失败: {str(e)}"}
    
    def upload_device_status(self, device_id, status="在线", battery=100, network="良好", delay=20):
        """上传设备状态"""
        try:
            # 确保有有效的认证令牌
            if not self.ensure_valid_token():
                return {"code": 401, "message": "认证失败，无法获取有效令牌"}
                
            url = f"{self.server_url}/v1/update_status"
            timestamp = int(time.time() * 1000)  # 13位毫秒级时间戳
            data = {
                "device_id": device_id,
                "timestamp": timestamp,
                "Power": {
                    "state": "ON",
                    "battery_level": battery,
                    "isCharging": "Discharging" if battery < 100 else "Full"
                },
                "Net": {
                    "Wifi_state": "Connected",
                    "net_delay": delay
                },
                "Audio": {
                    "effect": "Normal",
                    "mode": "Standard"
                }
            }
            # 设备状态上报需要认证
            headers = {"Authorization": f"Bearer {self.token}"}
            response = requests.post(url, json=data, headers=headers)
            return response.json()
        except Exception as e:
            return {"code": 500, "message": f"请求失败: {str(e)}"}
    
    def clear_table(self, table_name, admin_id="1"):
        """清除指定表的数据（需要管理员权限）"""
        try:
            url = f"{self.server_url}/v1/admin/clear_table"
            data = {"table_name": table_name}
            headers = {"adminID": admin_id}
            response = requests.post(url, json=data, headers=headers)
            return response.json()
        except Exception as e:
            return {"code": 500, "message": f"请求失败: {str(e)}"}

    def upload_listen_data_with_timestamp(self, device_id, user_id, song_id, song_name, artists, duration, timestamp):
        """使用指定时间戳上传收听数据"""
        try:
            url = f"{self.server_url}/v2/data"
            data = {
                "device_id": device_id,
                "user_id": user_id,
                "timestamp": timestamp,
                "id": song_id,
                "song_name": song_name,
                "artists": artists,
                "dt": duration * 1000  # 转换为毫秒
            }
            # 听歌记录不需要认证
            headers = {}
            response = requests.post(url, json=data, headers=headers)
            return response.json()
        except Exception as e:
            return {"code": 500, "message": f"请求失败: {str(e)}"}
    
    def upload_device_status_with_timestamp(self, device_id, timestamp, status="在线", battery=100, network="良好", delay=20):
        """使用指定时间戳上传设备状态"""
        try:
            # 确保有有效的认证令牌
            if not self.ensure_valid_token():
                return {"code": 401, "message": "认证失败，无法获取有效令牌"}
                
            url = f"{self.server_url}/v1/update_status"
            data = {
                "device_id": device_id,
                "timestamp": timestamp,
                "Power": {
                    "state": "ON",
                    "battery_level": battery,
                    "isCharging": "Discharging" if battery < 100 else "Full"
                },
                "Net": {
                    "Wifi_state": "Connected",
                    "net_delay": delay
                },
                "Audio": {
                    "effect": "Normal",
                    "mode": "Standard"
                }
            }
            # 设备状态上报需要认证
            headers = {"Authorization": f"Bearer {self.token}"}
            response = requests.post(url, json=data, headers=headers)
            return response.json()
        except Exception as e:
            return {"code": 500, "message": f"请求失败: {str(e)}"}

    def direct_update_device_status(self, device_id, timestamp, status="在线", battery=100, network="良好", delay=20):
        """直接更新设备状态到MySQL和MongoDB"""
        try:
            # 获取数据库配置
            settings = QSettings("SmartSound", "DataGenerator")
            mysql_host = settings.value("mysql/host", "47.108.81.229")
            mysql_port = int(settings.value("mysql/port", "3306"))
            mysql_user = settings.value("mysql/user", "Smart_Sounddb")
            mysql_password = settings.value("mysql/password", "123456")
            mysql_database = settings.value("mysql/database", "Smart_Sounddb")
            
            mongo_host = settings.value("mongo/host", "47.108.81.229")
            mongo_port = int(settings.value("mongo/port", "27017"))
            mongo_db = settings.value("mongo/database", "smartsound")
            mongo_user = settings.value("mongo/user", "smartsound")
            mongo_password = settings.value("mongo/password", "123456")
            mongo_auth_source = settings.value("mongo/auth_source", "smartsound")
            
            # 检查设备是否已激活
            is_activated = self.is_device_activated(device_id)
            
            # 如果设备未激活，则更新MySQL中的设备激活状态
            if not is_activated:
                try:
                    # 使用pymysql直接连接
                    conn = pymysql.connect(
                        host=mysql_host,
                        port=mysql_port,
                        user=mysql_user,
                        password=mysql_password,
                        database=mysql_database,
                        charset='utf8mb4',
                        cursorclass=pymysql.cursors.DictCursor
                    )
                    
                    try:
                        with conn.cursor() as cursor:
                            # 更新设备激活状态
                            cursor.execute(
                                "UPDATE device SET is_activated = 1, activation_time = %s WHERE device_id = %s",
                                (datetime.datetime.now(), device_id)
                            )
                            conn.commit()
                            print(f"已更新设备 {device_id} 的激活状态")
                    finally:
                        conn.close()
                except Exception as e:
                    print(f"MySQL更新失败: {str(e)}")
                    return {"code": 500, "message": f"MySQL更新失败: {str(e)}"}
            
            # 更新MongoDB中的设备状态
            try:
                # 构建MongoDB连接URI
                mongo_uri = f"mongodb://{mongo_user}:{mongo_password}@{mongo_host}:{mongo_port}/{mongo_db}?authSource={mongo_auth_source}"
                
                # 连接MongoDB
                client = MongoClient(mongo_uri)
                db = client[mongo_db]
                collection = db["device_status"]
                
                # 将毫秒级时间戳转换为datetime对象
                timestamp_dt = datetime.datetime.fromtimestamp(timestamp / 1000)
                
                # 准备MongoDB文档
                mongo_doc = {
                    "device_id": device_id,
                    "timestamp": timestamp_dt,
                    "received_at": datetime.datetime.now(),
                    "connection_status": "ONLINE",  # 连接状态：在线/离线
                    "activation_status": "ACTIVATED",  # 激活状态：激活/未激活
                    "power": {
                        "state": "ON",
                        "battery_level": battery,
                        "charging_state": "Discharging" if battery < 100 else "Full"
                    },
                    "network": {
                        "wifi_state": "Connected",
                        "net_delay": delay
                    },
                    "audio": {
                        "effect": "Normal",
                        "mode": "Standard"
                    }
                }
                
                # 插入到MongoDB
                result = collection.insert_one(mongo_doc)
                client.close()
                
                print(f"已向MongoDB插入设备 {device_id} 的状态数据")
                return {"code": 0, "message": "设备状态已更新", "mongo_id": str(result.inserted_id)}
            except Exception as e:
                print(f"MongoDB更新失败: {str(e)}")
                return {"code": 500, "message": f"MongoDB更新失败: {str(e)}"}
        except Exception as e:
            return {"code": 500, "message": f"更新设备状态失败: {str(e)}"}
            
    def is_device_activated(self, device_id):
        """检查设备是否已激活"""
        try:
            # 获取数据库配置
            settings = QSettings("SmartSound", "DataGenerator")
            mysql_host = settings.value("mysql/host", "47.108.81.229")
            mysql_port = int(settings.value("mysql/port", "3306"))
            mysql_user = settings.value("mysql/user", "Smart_Sounddb")
            mysql_password = settings.value("mysql/password", "123456")
            mysql_database = settings.value("mysql/database", "Smart_Sounddb")
            
            # 使用pymysql直接连接
            conn = pymysql.connect(
                host=mysql_host,
                port=mysql_port,
                user=mysql_user,
                password=mysql_password,
                database=mysql_database,
                charset='utf8mb4',
                cursorclass=pymysql.cursors.DictCursor
            )
            
            try:
                with conn.cursor() as cursor:
                    # 查询设备是否存在
                    cursor.execute(
                        "SELECT device_id, is_activated FROM device WHERE device_id = %s",
                        (device_id,)
                    )
                    result = cursor.fetchone()
                    
                    if result:
                        return bool(result['is_activated'])
                    else:
                        return None  # 设备不存在返回None
            finally:
                conn.close()
        except Exception as e:
            print(f"查询设备激活状态失败: {str(e)}")
            return False
            
    def direct_add_listen_record(self, device_id, user_id, song_id, song_name, artists, duration, timestamp):
        """直接向MongoDB添加收听记录"""
        try:
            # 获取MongoDB配置
            settings = QSettings("SmartSound", "DataGenerator")
            mongo_host = settings.value("mongo/host", "47.108.81.229")
            mongo_port = int(settings.value("mongo/port", "27017"))
            mongo_db = settings.value("mongo/database", "smartsound")
            mongo_user = settings.value("mongo/user", "smartsound")
            mongo_password = settings.value("mongo/password", "123456")
            mongo_auth_source = settings.value("mongo/auth_source", "smartsound")
            
            # 构建MongoDB连接URI
            mongo_uri = f"mongodb://{mongo_user}:{mongo_password}@{mongo_host}:{mongo_port}/{mongo_db}?authSource={mongo_auth_source}"
            
            # 连接MongoDB
            client = MongoClient(mongo_uri)
            db = client[mongo_db]
            collection = db["listen_records"]
            
            # 将毫秒级时间戳转换为datetime对象
            timestamp_dt = datetime.datetime.fromtimestamp(timestamp / 1000)
            date_str = timestamp_dt.strftime('%Y-%m-%d')
            
            # 获取时段基于设备上传的时间戳
            hour = timestamp_dt.hour
            if 5 <= hour < 12:
                period = "morning"
            elif 12 <= hour < 18:
                period = "afternoon"
            elif 18 <= hour < 22:
                period = "evening"
            else:
                period = "night"
                
            # 准备艺术家信息
            artist_names = []
            for artist in artists:
                artist_names.append(artist["name"])
            
            # 准备MongoDB文档
            mongo_doc = {
                "device_id": device_id,
                "user_id": user_id,
                "song_id": song_id,
                "song_name": song_name,
                "artists": artist_names,
                "duration": duration,
                "timestamp": timestamp_dt,
                "date": date_str,
                "period": period,
                "hour": hour
            }
            
            # 插入到MongoDB
            result = collection.insert_one(mongo_doc)
            client.close()
            
            print(f"已向MongoDB插入收听记录: {song_name}")
            return {"code": 0, "message": "收听记录已添加", "mongo_id": str(result.inserted_id)}
        except Exception as e:
            print(f"添加收听记录失败: {str(e)}")
            return {"code": 500, "message": f"添加收听记录失败: {str(e)}"}

class DeviceDataGeneratorThread(QThread):
    """单个设备的数据生成线程"""
    update_signal = pyqtSignal(dict)
    finished_signal = pyqtSignal(dict)
    
    def __init__(self, api_client, device_info, config):
        super().__init__()
        self.api_client = api_client
        self.device_info = device_info
        self.config = config
        self.is_running = True
    
    def run(self):
        try:
            device_id = self.device_info['device_id']
            user_id = self.device_info['user_id']
            device_key = self.device_info.get('device_key')
            records_per_device = self.config.get("records_per_device", 100)
            start_date = self.config.get("start_date")
            end_date = self.config.get("end_date")
            distribution_mode = self.config.get("distribution_mode", "均匀分布")
            weekday_weights = self.config.get("weekday_weights", {})
            
            # 获取时间段权重
            time_weights = self.config.get("time_weights", {
                "morning": 0.2,    # 早上 6-9点
                "noon": 0.15,      # 中午 11-14点
                "afternoon": 0.25, # 下午 15-18点
                "evening": 0.4     # 晚上 19-23点
            })
            
            # 由于我们现在直接访问数据库，不再需要获取token进行身份验证
            # 直接检查设备是否存在
            is_activated = self.api_client.is_device_activated(device_id)
            if is_activated is None:
                self.update_signal.emit({
                    "device_id": device_id,
                    "current": 0,
                    "total": records_per_device,
                    "result": {
                        "code": 404,
                        "message": f"设备 {device_id} 不存在"
                    }
                })
                self.finished_signal.emit({
                    "success": False,
                    "device_id": device_id,
                    "message": f"设备 {device_id} 不存在，无法生成数据"
                })
                return
            
            # 生成随机歌曲数据
            songs = []
            for i in range(100):  # 生成100首歌曲
                songs.append({
                    "id": i + 1,
                    "name": fake.catch_phrase(),
                    "duration": random.randint(180, 360),  # 3-6分钟
                    "artists": [{
                        "id": random.randint(1, 50),
                        "name": fake.name()
                    } for _ in range(random.randint(1, 3))]  # 1-3个艺术家
                })
            
            # 为设备生成记录
            for i in range(records_per_device):
                if not self.is_running:
                    break
                
                # 随机选择一首歌
                song = random.choice(songs)
                
                # 根据分布模式选择日期
                date_range = (end_date - start_date).days
                if date_range <= 0:
                    random_date = start_date
                else:
                    if distribution_mode == "均匀分布":
                        # 在时间范围内均匀分布时间点，但增加一些随机性
                        progress = (i + 1) / records_per_device
                        random_offset = random.uniform(-0.1, 0.1) * date_range
                        random_day = max(0, min(date_range, int(date_range * progress + random_offset)))
                        random_date = start_date + datetime.timedelta(days=random_day)
                    elif distribution_mode == "工作日偏多":
                        # 工作日的概率更高
                        random_day = random.randint(0, date_range)
                        random_date_temp = start_date + datetime.timedelta(days=random_day)
                        # 如果是周末，有60%的概率重新选择工作日
                        if random_date_temp.weekday() >= 5 and random.random() < 0.6:
                            # 重新选择一个工作日
                            workday_offset = random.randint(0, date_range)
                            temp_date = start_date + datetime.timedelta(days=workday_offset)
                            while temp_date.weekday() >= 5:
                                workday_offset = random.randint(0, date_range)
                                temp_date = start_date + datetime.timedelta(days=workday_offset)
                            random_day = workday_offset
                            random_date = temp_date
                        else:
                            random_date = random_date_temp
                    elif distribution_mode == "周末偏多":
                        # 周末的概率更高
                        random_day = random.randint(0, date_range)
                        random_date_temp = start_date + datetime.timedelta(days=random_day)
                        # 如果是工作日，有60%的概率重新选择周末
                        if random_date_temp.weekday() < 5 and random.random() < 0.6:
                            # 重新选择一个周末
                            weekend_offset = random.randint(0, date_range)
                            temp_date = start_date + datetime.timedelta(days=weekend_offset)
                            while temp_date.weekday() < 5:
                                weekend_offset = random.randint(0, date_range)
                                temp_date = start_date + datetime.timedelta(days=weekend_offset)
                            random_day = weekend_offset
                            random_date = temp_date
                        else:
                            random_date = random_date_temp
                    elif distribution_mode == "自定义分布" and weekday_weights:
                        # 使用自定义星期权重
                        # 将星期权重映射到0-6的索引
                        weekday_map = {
                            "monday": 0,
                            "tuesday": 1,
                            "wednesday": 2,
                            "thursday": 3,
                            "friday": 4,
                            "saturday": 5,
                            "sunday": 6
                        }
                        
                        # 创建权重列表，对应周一到周日
                        weights = [0] * 7
                        for day, weight in weekday_weights.items():
                            if day in weekday_map:
                                weights[weekday_map[day]] = weight
                        
                        # 如果所有权重都是0，则使用均匀分布
                        if sum(weights) == 0:
                            weights = [1/7] * 7
                        
                        # 选择一个星期几
                        target_weekday = random.choices(range(7), weights=weights, k=1)[0]
                        
                        # 在日期范围内找到符合目标星期几的日期
                        possible_dates = []
                        for day in range(date_range + 1):
                            temp_date = start_date + datetime.timedelta(days=day)
                            if temp_date.weekday() == target_weekday:
                                possible_dates.append(temp_date)
                        
                        if possible_dates:
                            random_date = random.choice(possible_dates)
                        else:
                            # 如果没有找到符合的日期，则随机选择一个日期
                            random_day = random.randint(0, date_range)
                            random_date = start_date + datetime.timedelta(days=random_day)
                    else:  # 随机分布
                        random_day = random.randint(0, date_range)
                        random_date = start_date + datetime.timedelta(days=random_day)
                
                # 生成更符合真实听歌习惯的时间段
                # 早上：6-9点，中午：11-14点，下午：15-18点，晚上：19-23点
                time_periods = [
                    (6, 9),    # 早上
                    (11, 14),  # 中午
                    (15, 18),  # 下午
                    (19, 23)   # 晚上
                ]
                period_names = ["morning", "noon", "afternoon", "evening"]
                period_weights = [time_weights[name] for name in period_names]
                
                # 根据权重选择时间段
                selected_period_index = random.choices(range(len(time_periods)), weights=period_weights, k=1)[0]
                selected_period = time_periods[selected_period_index]
                random_hour = random.randint(selected_period[0], selected_period[1])
                random_minute = random.randint(0, 59)
                random_second = random.randint(0, 59)
                
                random_datetime = datetime.datetime(
                    random_date.year, random_date.month, random_date.day,
                    random_hour, random_minute, random_second
                )
                
                # 转换为时间戳 (使用生成的随机时间而不是当前时间)
                timestamp = int(random_datetime.timestamp() * 1000)
                
                # 生成更真实的设备状态数据
                # 电池电量随时间递减，每天减少约20%
                day_progress = random_day / date_range if date_range > 0 else 0
                base_battery = 100 - int(day_progress * 20)
                battery = max(20, base_battery - random.randint(0, 15))  # 最低20%
                
                # 网络延迟根据时间段有所不同（晚上网络更拥堵）
                if 19 <= random_hour <= 23:  # 晚上
                    delay = random.randint(50, 200)
                elif 12 <= random_hour <= 14:  # 中午
                    delay = random.randint(30, 150)
                else:  # 其他时间
                    delay = random.randint(10, 100)
                
                network_quality = "良好"
                if delay > 150:
                    network_quality = "较差"
                elif delay > 80:
                    network_quality = "一般"
                
                # 同步上传数据和设备状态
                try:
                    # 直接添加收听记录到MongoDB，而不是通过API
                    data_result = self.api_client.direct_add_listen_record(
                        device_id, user_id, song["id"], song["name"],
                        song["artists"], song["duration"], timestamp
                    )
                    
                    # 直接更新设备状态到数据库，而不是通过API
                    status_result = self.api_client.direct_update_device_status(
                        device_id, timestamp, battery=battery, network=network_quality, delay=delay
                    )
                    
                    # 更新进度
                    progress = {
                        "device_id": device_id,
                        "current": i + 1,
                        "total": records_per_device,
                        "result": {
                            "data": data_result,
                            "status": status_result
                        }
                    }
                    self.update_signal.emit(progress)
                    
                except Exception as e:
                    self.update_signal.emit({
                        "device_id": device_id,
                        "current": i + 1,
                        "total": records_per_device,
                        "result": {
                            "code": 500,
                            "message": f"上传数据失败: {str(e)}"
                        }
                    })
                
                # 随机延迟，避免请求过快
                time.sleep(random.uniform(0.1, 0.5))
            
            self.finished_signal.emit({
                "success": True,
                "device_id": device_id,
                "message": f"设备 {device_id} 成功生成{records_per_device}条数据"
            })
        except Exception as e:
            self.finished_signal.emit({
                "success": False,
                "device_id": device_id,
                "message": f"设备 {device_id} 生成数据失败: {str(e)}"
            })
    
    def stop(self):
        self.is_running = False

class DataGeneratorThread(QThread):
    """数据生成主线程"""
    update_signal = pyqtSignal(dict)
    finished_signal = pyqtSignal(dict)
    
    def __init__(self, api_client, config):
        super().__init__()
        self.api_client = api_client
        self.config = config
        self.is_running = True
        self.device_threads = []
    
    def run(self):
        try:
            min_records = self.config.get("min_records_per_device", 10)
            max_records = self.config.get("max_records_per_device", 100)
            start_date = self.config.get("start_date")
            end_date = self.config.get("end_date")
            distribution_mode = self.config.get("distribution_mode", "均匀分布")
            weekday_weights = self.config.get("weekday_weights", {})
            time_weights = self.config.get("time_weights", {
                "morning": 0.2,
                "noon": 0.15,
                "afternoon": 0.25,
                "evening": 0.4
            })
            
            # 直接从device表查询已绑定用户的设备
            settings = QSettings("SmartSound", "DataGenerator")
            mysql_host = settings.value("mysql/host", "47.108.81.229")
            mysql_port = settings.value("mysql/port", "3306")
            mysql_user = settings.value("mysql/user", "Smart_Sounddb")
            mysql_password = settings.value("mysql/password", "123456")
            mysql_database = settings.value("mysql/database", "Smart_Sounddb")
            
            # 构建MySQL连接URI
            mysql_uri = f"mysql+pymysql://{mysql_user}:{mysql_password}@{mysql_host}:{mysql_port}/{mysql_database}"
            
            # 连接MySQL
            engine = create_engine(mysql_uri)
            Session = sessionmaker(bind=engine)
            session = Session()
            
            # 查询已绑定用户的设备，包括设备密钥
            query = text("""
                SELECT d.device_id, d.user_id, d.device_key
                FROM device d
                WHERE d.user_id IS NOT NULL
            """)
            result = session.execute(query)
            
            devices = []
            for row in result:
                devices.append({
                    'device_id': row.device_id,
                    'user_id': row.user_id,
                    'device_key': row.device_key
                })
            
            session.close()
            
            if not devices:
                self.finished_signal.emit({
                    "success": False,
                    "message": "未找到已绑定用户的设备"
                })
                return
            
            self.update_signal.emit({
                "current": 0,
                "total": len(devices),
                "result": {"code": 0, "message": f"找到 {len(devices)} 个已绑定用户的设备"}
            })
            
            # 为每个设备分配随机数量的记录
            device_records = {}
            total_records = 0
            
            for device in devices:
                # 为每个设备随机分配记录数量
                device_id = device['device_id']
                records = random.randint(min_records, max_records)
                device_records[device_id] = records
                total_records += records
            
            # 创建并启动设备数据生成线程
            for device in devices:
                if not self.is_running:
                    break
                
                device_id = device['device_id']
                if device_id not in device_records or device_records[device_id] <= 0:
                    continue
                
                # 创建设备数据生成线程
                device_thread = DeviceDataGeneratorThread(
                    self.api_client,
                    device,
                    {
                        "records_per_device": device_records[device_id],
                        "start_date": start_date,
                        "end_date": end_date,
                        "distribution_mode": distribution_mode,
                        "weekday_weights": weekday_weights,
                        "time_weights": time_weights
                    }
                )
                
                # 连接信号
                device_thread.update_signal.connect(self.update_progress)
                device_thread.finished_signal.connect(self.device_finished)
                
                # 启动线程
                device_thread.start()
                self.device_threads.append(device_thread)
            
            # 等待所有设备线程完成
            for thread in self.device_threads:
                thread.wait()
            
            self.finished_signal.emit({"success": True, "message": f"成功生成{total_records}条数据，分布在{len(devices)}个设备上"})
        except Exception as e:
            self.finished_signal.emit({"success": False, "message": f"生成数据失败: {str(e)}"})
    
    def update_progress(self, progress):
        """更新进度"""
        # 将设备线程的进度信号转发出去
        self.update_signal.emit(progress)
    
    def device_finished(self, result):
        """设备数据生成完成"""
        if not result["success"]:
            self.update_signal.emit({
                "result": {"code": 500, "message": result["message"]}
            })
    
    def stop(self):
        """停止所有设备线程"""
        self.is_running = False
        for thread in self.device_threads:
            thread.stop()
            thread.wait()

class MainWindow(QMainWindow):
    """主窗口"""
    
    def __init__(self):
        super().__init__()
        self.api_client = ApiClient()
        self.data_thread = None
        self.batch_thread = None
        self.init_ui()
        self.apply_style()
    
    def apply_style(self):
        """应用样式"""
        # 设置窗口样式
        self.setStyleSheet("""
            QMainWindow {
                background-color: #f5f5f5;
            }
            QGroupBox {
                font-weight: bold;
                border: 1px solid #cccccc;
                border-radius: 5px;
                margin-top: 1ex;
                padding: 10px;
                background-color: #ffffff;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 3px 0 3px;
                color: #2196F3;
            }
            QPushButton {
                background-color: #2196F3;
                color: white;
                border: none;
                padding: 8px 15px;
                border-radius: 4px;
                font-weight: bold;
            }
            QPushButton:hover {
                background-color: #1976D2;
            }
            QPushButton:disabled {
                background-color: #BDBDBD;
            }
            QLineEdit, QSpinBox, QDateEdit, QComboBox {
                padding: 6px;
                border: 1px solid #BDBDBD;
                border-radius: 4px;
                background-color: #FFFFFF;
            }
            QProgressBar {
                border: 1px solid #BDBDBD;
                border-radius: 4px;
                text-align: center;
                background-color: #FFFFFF;
                height: 20px;
            }
            QProgressBar::chunk {
                background-color: #2196F3;
                border-radius: 3px;
            }
            QTextEdit {
                border: 1px solid #BDBDBD;
                border-radius: 4px;
                padding: 5px;
                background-color: #FFFFFF;
            }
            QLabel {
                color: #424242;
            }
            QTabWidget::pane {
                border: 1px solid #BDBDBD;
                border-radius: 4px;
                background-color: #FFFFFF;
            }
            QTabBar::tab {
                background-color: #E0E0E0;
                color: #424242;
                padding: 8px 15px;
                margin-right: 2px;
                border-top-left-radius: 4px;
                border-top-right-radius: 4px;
            }
            QTabBar::tab:selected {
                background-color: #2196F3;
                color: white;
            }
        """)
    
    def init_ui(self):
        """初始化UI"""
        self.setWindowTitle("Smart Sound 数据生成器")
        self.setGeometry(100, 100, 1000, 800)
        
        # 创建菜单栏
        menubar = self.menuBar()
        config_menu = menubar.addMenu("配置")
        
        # 添加配置动作
        config_action = config_menu.addAction("后端与数据库配置")
        config_action.triggered.connect(self.show_config_dialog)
        
        # 创建标签页
        self.tabs = QTabWidget()
        self.setCentralWidget(self.tabs)
        
        # 创建各个标签页
        self.create_user_tab()
        self.create_data_generator_tab()
        self.create_table_cleaner_tab()
    
    def show_config_dialog(self):
        """显示配置对话框"""
        dialog = ConfigDialog(self)
        if dialog.exec_() == QDialog.Accepted:
            self.api_client.load_settings()
    
    def create_user_tab(self):
        """创建用户标签页"""
        user_tab = QWidget()
        layout = QVBoxLayout()
        
        # 批量用户注册组
        batch_group = QGroupBox("批量用户注册与设备绑定")
        batch_layout = QFormLayout()
        
        self.batch_count_input = QSpinBox()
        self.batch_count_input.setRange(1, 1000)
        self.batch_count_input.setValue(10)
        self.batch_count_input.setFixedWidth(150)
        
        # 用户名前缀
        self.username_prefix_input = QLineEdit()
        self.username_prefix_input.setPlaceholderText("用户名前缀（可选）")
        self.username_prefix_input.setFixedWidth(200)
        
        # 设备ID前缀
        self.device_prefix_input = QLineEdit()
        self.device_prefix_input.setPlaceholderText("设备ID前缀（可选）")
        self.device_prefix_input.setText("DEV")
        self.device_prefix_input.setFixedWidth(200)
        
        self.batch_start_btn = QPushButton("开始批量操作")
        self.batch_start_btn.clicked.connect(self.start_batch_operation)
        
        self.batch_stop_btn = QPushButton("停止批量操作")
        self.batch_stop_btn.clicked.connect(self.stop_batch_operation)
        self.batch_stop_btn.setEnabled(False)
        
        batch_layout.addRow("批量数量:", self.batch_count_input)
        batch_layout.addRow("用户名前缀:", self.username_prefix_input)
        batch_layout.addRow("设备ID前缀:", self.device_prefix_input)
        
        batch_button_layout = QHBoxLayout()
        batch_button_layout.addWidget(self.batch_start_btn)
        batch_button_layout.addWidget(self.batch_stop_btn)
        batch_layout.addRow("", batch_button_layout)
        
        # 批量操作进度条
        self.batch_progress_bar = QProgressBar()
        self.batch_progress_bar.setRange(0, 100)
        self.batch_progress_bar.setValue(0)
        batch_layout.addRow("批量进度:", self.batch_progress_bar)
        
        batch_group.setLayout(batch_layout)
        
        # 说明信息
        info_label = QLabel("说明：批量操作会自动生成随机用户名，使用固定密码123456，并为每个用户自动生成设备ID和密钥进行绑定。")
        info_label.setWordWrap(True)
        info_label.setStyleSheet("QLabel { color: #666; font-style: italic; padding: 10px; }")
        
        # 状态显示
        self.status_text = QTextEdit()
        self.status_text.setReadOnly(True)
        
        # 添加到布局
        layout.addWidget(batch_group)
        layout.addWidget(info_label)
        layout.addWidget(QLabel("操作日志:"))
        layout.addWidget(self.status_text)
        
        user_tab.setLayout(layout)
        self.tabs.addTab(user_tab, "批量操作")
    
    def create_data_generator_tab(self):
        """创建数据生成标签页"""
        generator_tab = QWidget()
        layout = QVBoxLayout()
        
        # 配置组
        config_group = QGroupBox("数据生成配置")
        config_layout = QFormLayout()
        
        self.min_records_per_device = QSpinBox()
        self.min_records_per_device.setRange(1, 10000)
        self.min_records_per_device.setValue(10)
        self.min_records_per_device.setSingleStep(10)
        self.min_records_per_device.setFixedWidth(150)
        
        self.max_records_per_device = QSpinBox()
        self.max_records_per_device.setRange(1, 10000)
        self.max_records_per_device.setValue(100)
        self.max_records_per_device.setSingleStep(10)
        self.max_records_per_device.setFixedWidth(150)
        
        self.start_date_input = QDateEdit()
        self.start_date_input.setDate(QDate.currentDate().addDays(-30))
        self.start_date_input.setCalendarPopup(True)
        self.start_date_input.setFixedWidth(150)
        
        self.end_date_input = QDateEdit()
        self.end_date_input.setDate(QDate.currentDate())
        self.end_date_input.setCalendarPopup(True)
        self.end_date_input.setFixedWidth(150)
        
        # 添加高级选项
        advanced_group = QGroupBox("高级选项")
        advanced_layout = QFormLayout()
        
        # 数据分布模式
        self.distribution_combo = QComboBox()
        self.distribution_combo.addItems(["均匀分布", "工作日偏多", "周末偏多", "随机分布", "自定义分布"])
        self.distribution_combo.setCurrentIndex(0)
        self.distribution_combo.setFixedWidth(150)
        self.distribution_combo.currentIndexChanged.connect(self.on_distribution_changed)
        
        # 星期分布权重组
        self.weekday_group = QGroupBox("星期分布权重")
        weekday_layout = QFormLayout()
        
        self.weekday_weights = []
        weekday_names = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
        
        for i, name in enumerate(weekday_names):
            weight = QSpinBox()
            weight.setRange(0, 100)
            # 工作日默认15%，周末默认10%
            default_value = 10 if i >= 5 else 15
            weight.setValue(default_value)
            weight.setSuffix("%")
            weight.setFixedWidth(80)
            self.weekday_weights.append(weight)
            weekday_layout.addRow(f"{name}:", weight)
        
        self.weekday_group.setLayout(weekday_layout)
        self.weekday_group.setVisible(False)  # 默认隐藏
        
        # 时间段权重
        time_period_group = QGroupBox("时间段权重")
        time_period_layout = QFormLayout()
        
        self.morning_weight = QSpinBox()
        self.morning_weight.setRange(1, 100)
        self.morning_weight.setValue(20)
        self.morning_weight.setSuffix("%")
        self.morning_weight.setFixedWidth(80)
        
        self.noon_weight = QSpinBox()
        self.noon_weight.setRange(1, 100)
        self.noon_weight.setValue(15)
        self.noon_weight.setSuffix("%")
        self.noon_weight.setFixedWidth(80)
        
        self.afternoon_weight = QSpinBox()
        self.afternoon_weight.setRange(1, 100)
        self.afternoon_weight.setValue(25)
        self.afternoon_weight.setSuffix("%")
        self.afternoon_weight.setFixedWidth(80)
        
        self.evening_weight = QSpinBox()
        self.evening_weight.setRange(1, 100)
        self.evening_weight.setValue(40)
        self.evening_weight.setSuffix("%")
        self.evening_weight.setFixedWidth(80)
        
        time_period_layout.addRow("早上 (6-9点):", self.morning_weight)
        time_period_layout.addRow("中午 (11-14点):", self.noon_weight)
        time_period_layout.addRow("下午 (15-18点):", self.afternoon_weight)
        time_period_layout.addRow("晚上 (19-23点):", self.evening_weight)
        time_period_group.setLayout(time_period_layout)
        
        advanced_layout.addRow("数据分布:", self.distribution_combo)
        advanced_layout.addWidget(self.weekday_group)
        advanced_layout.addRow(time_period_group)
        advanced_group.setLayout(advanced_layout)
        
        # 添加直接访问数据库的提示
        db_mode_label = QLabel("数据生成模式: <b>直接访问数据库</b> (无需API认证)")
        db_mode_label.setStyleSheet("QLabel { color: #2196F3; }")
        
        config_layout.addRow("每设备最小记录数:", self.min_records_per_device)
        config_layout.addRow("每设备最大记录数:", self.max_records_per_device)
        config_layout.addRow("开始日期:", self.start_date_input)
        config_layout.addRow("结束日期:", self.end_date_input)
        config_layout.addRow(db_mode_label)
        config_layout.addRow(advanced_group)
        config_group.setLayout(config_layout)
        
        # 控制按钮
        self.start_btn = QPushButton("开始生成数据")
        self.start_btn.clicked.connect(self.start_data_generation)
        self.stop_btn = QPushButton("停止生成")
        self.stop_btn.clicked.connect(self.stop_data_generation)
        self.stop_btn.setEnabled(False)
        
        button_layout = QHBoxLayout()
        button_layout.addWidget(self.start_btn)
        button_layout.addWidget(self.stop_btn)
        
        # 进度条
        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, 100)
        self.progress_bar.setValue(0)
        
        # 日志显示
        log_group = QGroupBox("生成日志")
        log_layout = QVBoxLayout()
        self.log_text = QTextEdit()
        self.log_text.setReadOnly(True)
        log_layout.addWidget(self.log_text)
        log_group.setLayout(log_layout)
        
        # 添加到布局
        layout.addWidget(config_group)
        layout.addLayout(button_layout)
        layout.addWidget(QLabel("生成进度:"))
        layout.addWidget(self.progress_bar)
        layout.addWidget(log_group)
        
        generator_tab.setLayout(layout)
        self.tabs.addTab(generator_tab, "数据生成")
    
    def on_distribution_changed(self, index):
        """分布模式改变时的处理"""
        # 当选择"自定义分布"时显示星期权重设置
        self.weekday_group.setVisible(index == 4)  # 4是"自定义分布"的索引
    
    def create_table_cleaner_tab(self):
        """创建表清理标签页"""
        cleaner_tab = QWidget()
        layout = QVBoxLayout()
        
        # MongoDB清理组
        mongo_group = QGroupBox("MongoDB数据清理")
        mongo_layout = QVBoxLayout()
        
        self.clear_mongo_btn = QPushButton("清除所有MongoDB数据")
        self.clear_mongo_btn.clicked.connect(self.clear_mongodb_data)
        
        # MongoDB集合选择
        self.mongo_combo = QComboBox()
        mongo_collections = list(MONGODB_COLLECTIONS.values())
        self.mongo_combo.addItems(mongo_collections)
        self.mongo_combo.setCurrentIndex(0)
        
        self.clear_mongo_collection_btn = QPushButton("清除选定集合")
        self.clear_mongo_collection_btn.clicked.connect(lambda: self.clear_table("mongodb"))
        
        mongo_layout.addWidget(QLabel("清除所有MongoDB数据:"))
        mongo_layout.addWidget(self.clear_mongo_btn)
        mongo_layout.addWidget(QLabel("清除指定集合:"))
        mongo_layout.addWidget(self.mongo_combo)
        mongo_layout.addWidget(self.clear_mongo_collection_btn)
        mongo_group.setLayout(mongo_layout)
        
        # MySQL表选择
        mysql_group = QGroupBox("MySQL表")
        mysql_layout = QVBoxLayout()
        
        self.mysql_combo = QComboBox()
        mysql_tables = ["user", "device", "artist", "song", "style", "artist_song", "song_style", "admin"]
        self.mysql_combo.addItems(mysql_tables)
        
        self.clear_mysql_btn = QPushButton("清除MySQL表数据")
        self.clear_mysql_btn.clicked.connect(lambda: self.clear_table("mysql"))
        
        mysql_layout.addWidget(QLabel("选择表:"))
        mysql_layout.addWidget(self.mysql_combo)
        mysql_layout.addWidget(self.clear_mysql_btn)
        mysql_group.setLayout(mysql_layout)
        
        # 管理员ID输入
        admin_group = QGroupBox("管理员认证")
        admin_layout = QFormLayout()
        
        self.admin_id_input = QLineEdit("1")
        self.admin_id_input.setPlaceholderText("管理员ID")
        self.admin_id_input.setFixedWidth(150)
        
        admin_layout.addRow("管理员ID:", self.admin_id_input)
        admin_group.setLayout(admin_layout)
        
        # 清理日志
        log_group = QGroupBox("清理日志")
        log_layout = QVBoxLayout()
        self.cleaner_log = QTextEdit()
        self.cleaner_log.setReadOnly(True)
        log_layout.addWidget(self.cleaner_log)
        log_group.setLayout(log_layout)
        
        # 添加到布局
        layout.addWidget(mongo_group)
        layout.addWidget(mysql_group)
        layout.addWidget(admin_group)
        layout.addWidget(log_group)
        
        cleaner_tab.setLayout(layout)
        self.tabs.addTab(cleaner_tab, "表清理")
    
    def clear_mongodb_data(self):
        """清除所有MongoDB数据"""
        reply = QMessageBox.question(
            self, "确认清除",
            "确定要清除所有MongoDB数据吗？此操作不可恢复！",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )
        
        if reply == QMessageBox.Yes:
            result = self.api_client.clear_mongodb_collections()
            if result.get("code") == 0:
                self.cleaner_log.append("✅ MongoDB数据清除成功")
                QMessageBox.information(self, "成功", "MongoDB数据已清除")
            else:
                self.cleaner_log.append(f"❌ MongoDB数据清除失败: {result.get('message')}")
                QMessageBox.warning(self, "错误", f"清除失败: {result.get('message')}")
    
    def start_data_generation(self):
        """开始生成数据"""
        try:
            min_records = self.min_records_per_device.value()
            max_records = self.max_records_per_device.value()
            start_date = self.start_date_input.date().toPyDate()
            end_date = self.end_date_input.date().toPyDate()
            
            if start_date > end_date:
                QMessageBox.warning(self, "日期错误", "开始日期不能晚于结束日期")
                return
                
            if min_records > max_records:
                QMessageBox.warning(self, "记录数错误", "每设备最小记录数不能大于最大记录数")
                return
            
            # 获取高级选项
            distribution_mode = self.distribution_combo.currentText()
            
            # 获取星期分布权重
            weekday_weights = {}
            if distribution_mode == "自定义分布":
                total_weight = sum(weight.value() for weight in self.weekday_weights)
                if total_weight == 0:
                    QMessageBox.warning(self, "权重错误", "星期权重总和不能为0")
                    return
                    
                # 将权重转换为0-1之间的小数
                for i, day_name in enumerate(["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"]):
                    weekday_weights[day_name] = self.weekday_weights[i].value() / total_weight
            
            # 获取时间段权重
            time_weights = {
                "morning": self.morning_weight.value() / 100.0,
                "noon": self.noon_weight.value() / 100.0,
                "afternoon": self.afternoon_weight.value() / 100.0,
                "evening": self.evening_weight.value() / 100.0
            }
            
            # 检查权重总和是否接近1
            weight_sum = sum(time_weights.values())
            if abs(weight_sum - 1.0) > 0.05:  # 允许5%的误差
                # 自动调整权重
                factor = 1.0 / weight_sum
                for key in time_weights:
                    time_weights[key] *= factor
                
                self.log_text.append(f"注意: 时间段权重总和不为100%，已自动调整")
            
            # 如果已有线程在运行，先停止它
            if self.data_thread and self.data_thread.isRunning():
                self.data_thread.stop()
                self.data_thread.wait()
            
            # 创建新的数据生成线程
            self.data_thread = DataGeneratorThread(
                self.api_client,
                {
                    "min_records_per_device": min_records,
                    "max_records_per_device": max_records,
                    "start_date": start_date,
                    "end_date": end_date,
                    "distribution_mode": distribution_mode,
                    "weekday_weights": weekday_weights,
                    "time_weights": time_weights
                }
            )
            
            # 连接信号
            self.data_thread.update_signal.connect(self.update_progress)
            self.data_thread.finished_signal.connect(self.generation_finished)
            
            # 启动线程
            self.data_thread.start()
            
            # 更新UI状态
            self.start_btn.setEnabled(False)
            self.stop_btn.setEnabled(True)
            self.log_text.append(f"开始生成数据，时间范围：{start_date} 到 {end_date}")
            self.log_text.append(f"每设备记录数范围：{min_records} - {max_records}")
            self.log_text.append(f"分布模式：{distribution_mode}")
            
            if distribution_mode == "自定义分布":
                weekday_str = ", ".join([f"周{i+1}: {self.weekday_weights[i].value()}%" for i in range(7)])
                self.log_text.append(f"星期权重：{weekday_str}")
                
            self.log_text.append(f"时间段权重：早上 {time_weights['morning']:.2f}，中午 {time_weights['noon']:.2f}，下午 {time_weights['afternoon']:.2f}，晚上 {time_weights['evening']:.2f}")
            self.log_text.append(f"数据生成模式：直接访问数据库（无需API认证）")
            
        except Exception as e:
            QMessageBox.warning(self, "错误", f"启动数据生成失败: {str(e)}")
    
    def stop_data_generation(self):
        """停止生成数据"""
        if self.data_thread and self.data_thread.isRunning():
            self.data_thread.stop()
            self.log_text.append("正在停止数据生成...")
    
    def update_progress(self, progress):
        """更新进度"""
        try:
            current = progress.get("current", 0)
            total = progress.get("total", 0)
            result = progress.get("result", {})
            device_id = progress.get("device_id", "未知设备")
            
            # 更新进度条
            if total > 0:
                percent = int(current / total * 100)
                self.progress_bar.setValue(percent)
            
            # 更新日志
            if isinstance(result, dict) and "data" in result and "status" in result:
                data_status = "成功" if result["data"].get("code") == 0 else "失败"
                status_status = "成功" if result["status"].get("code") == 0 else "失败"
                
                # 只在出错时显示详细信息，减少日志量
                if result["data"].get("code") != 0 or result["status"].get("code") != 0:
                    self.log_text.append(f"[设备 {device_id}] [{current}/{total}] 数据上传{data_status}, 状态上报{status_status}")
                    
                    if result["data"].get("code") != 0:
                        self.log_text.append(f"数据错误: {json.dumps(result['data'], ensure_ascii=False)}")
                    
                    if result["status"].get("code") != 0:
                        self.log_text.append(f"状态错误: {json.dumps(result['status'], ensure_ascii=False)}")
                else:
                    # 每10条记录更新一次进度，避免日志过多
                    if current % 10 == 0 or current == total:
                        self.log_text.append(f"[设备 {device_id}] 进度: {current}/{total}")
            else:
                status = "成功" if result.get("code") == 0 else "失败"
                self.log_text.append(f"[设备 {device_id}] [{current}/{total}] 操作{status}: {json.dumps(result, ensure_ascii=False)}")
            
            # 自动滚动到底部
            self.log_text.verticalScrollBar().setValue(self.log_text.verticalScrollBar().maximum())
        except Exception as e:
            self.log_text.append(f"更新进度时出错: {str(e)}")
    
    def generation_finished(self, result):
        """数据生成完成"""
        try:
            # 更新UI状态
            self.start_btn.setEnabled(True)
            self.stop_btn.setEnabled(False)
            
            # 显示结果
            if result["success"]:
                self.log_text.append(f"✅ {result['message']}")
                QMessageBox.information(self, "完成", result["message"])
            else:
                self.log_text.append(f"❌ {result['message']}")
                QMessageBox.warning(self, "错误", result["message"])
        except Exception as e:
            self.log_text.append(f"处理完成信号时出错: {str(e)}")
    
    def closeEvent(self, event):
        """窗口关闭事件"""
        try:
            # 停止所有正在运行的线程
            if self.data_thread and self.data_thread.isRunning():
                self.data_thread.stop()
                self.data_thread.wait()
            event.accept()
        except Exception as e:
            print(f"关闭窗口时出错: {str(e)}")
            event.accept()

    def clear_table(self, db_type):
        """清除表数据"""
        admin_id = self.admin_id_input.text()
        
        if not admin_id:
            QMessageBox.warning(self, "输入错误", "管理员ID不能为空")
            return
        
        if db_type == "mysql":
            table_name = self.mysql_combo.currentText()
        else:  # mongodb
            table_name = self.mongo_combo.currentText()
        
        # 确认对话框
        reply = QMessageBox.question(
            self, "确认清除", f"确定要清除{db_type}数据库中的{table_name}表吗？",
            QMessageBox.Yes | QMessageBox.No, QMessageBox.No
        )
        
        if reply == QMessageBox.Yes:
            result = self.api_client.clear_table(table_name, admin_id)
            self.cleaner_log.append(f"清除{db_type}表{table_name}结果: {json.dumps(result, ensure_ascii=False, indent=2)}")
    
    def start_batch_operation(self):
        """开始批量操作"""
        try:
            count = self.batch_count_input.value()
            username_prefix = self.username_prefix_input.text()
            device_prefix = self.device_prefix_input.text()
            
            # 更新UI状态
            self.batch_start_btn.setEnabled(False)
            self.batch_stop_btn.setEnabled(True)
            self.batch_progress_bar.setValue(0)
            
            # 创建批量操作线程
            self.batch_thread = BatchOperationThread(
                self.api_client, 
                count, 
                {
                    "username_prefix": username_prefix,
                    "device_prefix": device_prefix
                }
            )
            self.batch_thread.update_signal.connect(self.update_batch_progress)
            self.batch_thread.finished_signal.connect(self.batch_operation_finished)
            self.batch_thread.start()
            
        except Exception as e:
            QMessageBox.warning(self, "错误", f"启动批量操作失败: {str(e)}")
    
    def stop_batch_operation(self):
        """停止批量操作"""
        if hasattr(self, 'batch_thread') and self.batch_thread.isRunning():
            self.batch_thread.stop()
            self.status_text.append("正在停止批量操作...")
    
    def update_batch_progress(self, progress):
        """更新批量操作进度"""
        try:
            current = progress.get("current", 0)
            total = progress.get("total", 0)
            result = progress.get("result", {})
            
            # 更新进度条
            if total > 0:
                percent = int(current / total * 100)
                self.batch_progress_bar.setValue(percent)
            
            # 更新日志
            status = "成功" if result.get("code") == 0 else "失败"
            self.status_text.append(f"[{current}/{total}] 操作{status}: {json.dumps(result, ensure_ascii=False)}")
            
            # 自动滚动到底部
            self.status_text.verticalScrollBar().setValue(self.status_text.verticalScrollBar().maximum())
        except Exception as e:
            self.status_text.append(f"更新进度时出错: {str(e)}")
    
    def batch_operation_finished(self, result):
        """批量操作完成"""
        try:
            # 更新UI状态
            self.batch_start_btn.setEnabled(True)
            self.batch_stop_btn.setEnabled(False)
            
            # 显示结果
            if result["success"]:
                self.status_text.append(f"✅ {result['message']}")
                QMessageBox.information(self, "完成", result["message"])
            else:
                self.status_text.append(f"❌ {result['message']}")
                QMessageBox.warning(self, "错误", result["message"])
        except Exception as e:
            self.status_text.append(f"处理完成信号时出错: {str(e)}")

class BatchOperationThread(QThread):
    """批量操作线程"""
    update_signal = pyqtSignal(dict)
    finished_signal = pyqtSignal(dict)
    
    def __init__(self, api_client, count, options):
        super().__init__()
        self.api_client = api_client
        self.count = count
        self.options = options
        self.is_running = True
    
    def run(self):
        try:
            success_count = 0
            for i in range(self.count):
                if not self.is_running:
                    break
                
                # 生成随机用户名，可以使用前缀
                username_prefix = self.options.get('username_prefix', '')
                if username_prefix:
                    username = f"{username_prefix}{random.randint(1000, 9999)}"
                else:
                    username = fake.user_name()
                
                # 生成设备ID和密钥
                device_prefix = self.options.get('device_prefix', 'DEV')
                device_id = f"{device_prefix}{random.randint(100000, 999999)}"
                device_key = secrets.token_hex(16)
        
                # 注册用户
                user_result = self.api_client.register_user(username)
                if user_result.get("code") != 0:
                    self.update_signal.emit({
                        "current": i + 1,
                        "total": self.count,
                        "result": user_result
                    })
                    continue
                
                user_id = user_result.get("data", {}).get("user_id")
                if not user_id:
                    continue
                
                # 注册设备
                device_result = self.api_client.register_device(device_id, device_key)
                if device_result.get("code") != 0:
                    self.update_signal.emit({
                        "current": i + 1,
                        "total": self.count,
                        "result": device_result
                    })
                    continue
                
                # 绑定设备
                bind_result = self.api_client.bind_device(device_id, user_id, device_key)
                if bind_result.get("code") == 0:
                    success_count += 1
                
                self.update_signal.emit({
                    "current": i + 1,
                    "total": self.count,
                    "result": bind_result
                })
                
                # 随机延迟，避免请求过快
                time.sleep(random.uniform(0.1, 0.5))
            
            self.finished_signal.emit({
                "success": True,
                "message": f"批量操作完成，成功创建{success_count}个用户和设备"
            })
        except Exception as e:
            self.finished_signal.emit({
                "success": False,
                "message": f"批量操作失败: {str(e)}"
            })
    
    def stop(self):
        self.is_running = False

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())